package com.cskaoyan.javase.thread._5pc._2demo.sync_method;

import java.lang.management.ThreadInfo;

/**
 * 测试生产者消费者模型
 * 我们需要模拟实现：
 * 生产者生产手机 --> 存入仓库 --> 消费者从仓库消费手机...
 *
 *
 * @since 11:37
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Test {
    public static void main(String[] args) {
        // 创建仓库对象,仓库对象是被生产者和消费者共享的
        Warehouse warehouse = new Warehouse();
        // 创建生产者和消费者线程
        new Thread(new Producer(warehouse), "生产者").start();
        new Thread(new Consumer(warehouse), "消费者").start();
    }
}

// 生产者模型
class Producer implements Runnable {
    // 持有仓库对象的引用作为成员变量,方便存入手机
    private final Warehouse warehouse;

    private static Phone[] phones = {
            new Phone("IPhone12", 4500),
            new Phone("华为P50", 5000),
            new Phone("小米13", 6000),
            new Phone("OPPO Reno ACE", 4000)
    };

    public Producer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    private Phone producePhone() {
        return phones[(int) (Math.random() * phones.length)];
    }

    @Override
    public void run() {
        while (true) {
            warehouse.producePhone(phones[((int) (Math.random() * phones.length))]);
        }
    }
}

// 消费者模型
class Consumer implements Runnable {
    // 持有仓库对象的引用作为成员变量,方便存入手机
    private final Warehouse warehouse;

    public Consumer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    @Override
    public void run() {
        while (true) {
            warehouse.consumePhone();
        }
    }
}

// 商品类型
class Phone {
    private String name;
    private double price;

    public Phone(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

// 仓库类型,用于存储商品
class Warehouse {
    // 用于被消费者和生产者进行操作的对象(生产者相当于给它赋值对象,消费者相当于给它赋值null)
    private Phone phone;


    // 消费者的功能放在这里,提供给消费者调用,它们的锁对象都是仓库对象this,可以实现线程同步,保证数据安全
    public synchronized void consumePhone() {
        // 1.判断仓库有没有手机
        if (isEmpty()) {
            // 3.如果没有手机,wait
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            // 2.如果有手机,消费掉,然后notify
            sellPhone();
            this.notify();
        }
    }

    // 生产者的功能放在这里,它们的锁对象都是仓库对象this,可以实现线程同步,保证数据安全
    // 传入的参数phone是被生产的手机
    public synchronized void producePhone(Phone phone) {
        // 1.判断仓库有没有手机
        if (isEmpty()) {
            // 3.如果没有手机,生产一个手机,放入仓库,notify
            pullPhone(phone);
            this.notify();
        } else {
            // 2.如果有手机,wait
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 提供两个方法,这两个方法用于仓库存入手机以及仓库出库手机
    private void pullPhone(Phone phone) {
        this.phone = phone;
        System.out.println(Thread.currentThread().getName() + "生产了一部手机:" + phone);
    }

    private void sellPhone() {
        Phone sellPhone = phone;
        phone = null;
        System.out.println(Thread.currentThread().getName() + "消费了一部手机:" + sellPhone);
    }

    // 提供一个判断仓库有无手机的方法,true表示没有手机,false表示有手机
    public boolean isEmpty() {
        return phone == null;
    }

}